/**
 * 为函数声明了一个泛型参数 T，并将 参数的类型 与 返回值类型 指向这个泛型参数。
 * 这样，在这个函数接收到参数时，T 会自动地被填充为这个参数的类型。
 * 这也就意味着不再需要预先确定参数的可能类型了，
 * 而在 返回值 与 参数类型 关联的情况下，可以通过 泛型参数 来进行运算。
*/
/**
 * 1、在基于参数类型进行填充泛型时，其类型信息会被推断到尽可能精确的程度，会推导到字面量类型。
 * 这是因为在直接传入一个值时，这个值是不会再被修改的，因此可以推导到最精确的程度。
 * 而如果使用一个变量作为参数，那么只会使用这个变量标注的类型
*/
function handle<T>(input: T): T { return }
const author = "hanxuming"; // 使用 const 声明，被推导为 "hanxuming"
let authorAge = 18; // 使用 let 声明，被推导为 number
handle(author); // 填充为字面量类型 "hanxuming"
//使用一个变量作为参数，那么只会使用这个变量标注的类型
handle(authorAge); // 填充为基础类型 number

//返回值类型 对 泛型参数 进行了一些操作，同样可以看到其 调用信息 符合预期
function swap<T, U>([start, end]: [T, U]): [U, T] {
    return [end, start];
}
const swapped1 = swap(["hanxuming", 599]);
const swapped2 = swap([null, 599]);
const swapped3 = swap([{ name: "hanxuming" }, {}]);

/**
 * 2、函数中的泛型同样存在约束与默认值
*/
//如上面 swap 函数，只想处理数字元组的情况：
function swapA<T extends number, U extends number>([start, end]: [T, U]): [U, T] {
    return [end, start];
}

/**
 * 3、函数的泛型参数也会被内部的逻辑消费，
*/
function handleA<T>(payload: T): Promise<[T]> {
    return new Promise<[T]>((res, rej) => {
        res([payload]);
    });
}
/**
 * 4、对于 箭头函数的泛型，其书写方式是这样的：
*/
const handleB = <T>(input: T): T => { return }

/**
 * 5、函数的泛型是日常使用较多的一部分，更明显地体现了 泛型在调用时被填充 这一特性，
 * 在 类型别名 中，更多是手动传入泛型。
 * 这一差异的缘由其实就是它们的场景不同，
 * 通常使用 类型别名 来对 已经确定的类型结构 进行类型操作，比如将一组确定的类型放置在一起。
 * 而在 函数 这种场景中，并不能确定泛型在实际运行时会被什么样的类型填充。
*/

//export {}：解决“无法重新声明块范围变量”错误提示问题
export { }